#include "yx020_port.h"
#include "uart_peripheral.h"
#include "timer_peripheral.h"
#include "delay.h"
#include "key.h"
#include "util_crc.h"
#include "stdio.h"

#define DELAY_OFFSET (-3)

struct YX020PortManager {
    uint8_t lowiq : 1;
    uint8_t bit8Mode : 1;
    uint8_t lock : 1;
    uint8_t reversed : 5;

    uint16_t delayCMD2D;
    uint16_t chipNum;
    uint32_t pdTime;
    uint32_t lowBaud;
    uint32_t highBaud;
    float magin;

    uint8_t *sendCache1;

    uint8_t const *cmd2sSeq;
    uint8_t cmd2sLen;
    uint8_t const *setIDSeq;
    uint8_t setIDLen;
    uint8_t const *vsyncSeq;
    uint8_t vsyncLen;
    uint8_t const *cmd2dSeq;
    uint8_t cmd2dLen;
    uint8_t const *setBaudSql;
    uint8_t setBaudLen;

    DelayMsFunction delayMs;
};

static struct YX020PortManager *sManager = NULL;

static void calDelayCMD2D(void)
{
    float baud = UartGetBaud(UART_COM1);
    float t = 500000.0f / baud;
    sManager->delayCMD2D = t * sManager->chipNum * sManager->magin;
}

void YX020PortInit(DelayMsFunction delayMs)
{
    static struct YX020PortManager manager;
    static uint8_t sendCache1[YX020PORT_SENDCACHE_SIZE];
    static uint8_t const cmd2sSeq[] = {0x55, 0xC0, 0xAA, 0x2B};
    static uint8_t const setIDSeq[] = {0x55, 0x20, 0x00, 0x8F, 0x00, 0x00, 0x00};
    static uint8_t const vsyncSeq[] = {0x55, 0x40, 0x00, 0xD5};
    static uint8_t const cmd2dSeq[] = {0x55, 0xC0, 0x55, 0x1E};
    static uint8_t const setbuadSeq[] = {0x55, 0xC0, 0x0A, 0x84};

    if (sManager == NULL) {
        manager.lowiq = 1;
        manager.bit8Mode = 0;
        manager.lock = 0;

        manager.chipNum = 0;
        manager.pdTime = 1000;
        manager.lowBaud = 800000;
        manager.highBaud = 2400000;

        manager.sendCache1 = NULL;

        manager.cmd2sSeq = cmd2sSeq;
        manager.cmd2sLen = sizeof(cmd2sSeq) / sizeof(cmd2sSeq[0]);
        manager.setIDSeq = setIDSeq;
        manager.setIDLen = sizeof(setIDSeq) / sizeof(setIDSeq[0]);
        manager.vsyncSeq = vsyncSeq;
        manager.vsyncLen = sizeof(vsyncSeq) / sizeof(vsyncSeq[0]);
        manager.cmd2dSeq = cmd2dSeq;
        manager.cmd2dLen = sizeof(cmd2dSeq) / sizeof(cmd2dSeq[0]);
        manager.setBaudSql = setbuadSeq;
        manager.setBaudLen = sizeof(setbuadSeq) / sizeof(setbuadSeq[0]);

        manager.delayMs = delayMs;

        sManager = &manager;
    }

    UartSetStopbit(UART_COM1, UART_STOPBIT_2);
}

void YX020PortSetDelayMs(DelayMsFunction delayMs)
{
    sManager->delayMs = delayMs;
}

void YX020PortSetLowiq(uint8_t status)
{
    if (status) {
        sManager->lowiq = 1;
    }
    else {
        sManager->lowiq = 0;
    }
}

void YX020PortSet8bitmode(uint8_t status)
{
    if (status) {
        sManager->bit8Mode = 1;
    }
	else {
		sManager->bit8Mode = 0;
	}
}

void YX020PortSetHLBaud(uint32_t low, uint32_t high)
{
	while (sManager->lock) {
	}
	sManager->lock = 1;
	
	sManager->lowBaud = low;
	sManager->highBaud = high;
	
	sManager->lock = 0;
}

void YX020PortTimerSetFre(uint32_t fre)
{
	Timer16SetFre(fre);
}

void YX020PortTimerRun(void)
{
	Timer16Run();
}

void YX020PortTimerStop(void)
{
	Timer16Stop();
}

uint8_t YX020PortCheckDir(void)
{
    if (KeyCheckInput(KEY_KEY0)) {
        return 1;
    }
    else {
        return 0;
    }
}

void YX020PortSetUartDir(uint8_t dir)
{
    if (dir) {
        UartRxTxExchangeEnable(UART_COM1);
    }
    else {
        UartRxTxExchangeDisable(UART_COM1);
    }
}

void YX020PortSendCMD2S(void)
{
    while (sManager->lock) {
    }
    sManager->lock = 1;

    UartRawSend(UART_COM1, sManager->cmd2sSeq, sManager->cmd2sLen);

    sManager->lock = 0;
}

void YX020PortSendCMD2D(void)
{
    while (sManager->lock) {
    }
    sManager->lock = 1;

    UartRawSend(UART_COM1, sManager->cmd2dSeq, sManager->cmd2dLen);

    sManager->lock = 0;
}

void YX020PortSendVSYNC(void)
{
    while (sManager->lock) {
    }
    sManager->lock = 1;

    UartRawSend(UART_COM1, sManager->vsyncSeq, sManager->vsyncLen);

    sManager->lock = 0;
}

void YX020PortSendSetBaud(void)
{
    while (sManager->lock) {
    }
    sManager->lock = 1;

    UartRawSend(UART_COM1, sManager->setBaudSql, sManager->setBaudLen);

    sManager->lock = 0;
}

void YX020PortSendSetID(uint16_t num)
{
    uint16_t i;

    while (sManager->lock) {
    }
    sManager->lock = 1;

    YX020PortSendCMD2S();
    UartWaitSendDone(UART_COM1);
    delayUs(200 + DELAY_OFFSET);

    for (i = 0; i < num; i++) {
        UartRawSend(UART_COM1, sManager->setIDSeq, sManager->setIDLen);
        delayUs(30 + DELAY_OFFSET);
    }

    sManager->lock = 0;
}

void YX020PortSendBrightness(uint16_t id, const uint16_t *br, uint16_t len)
{
    uint16_t i, j, cnt;
    uint16_t t;

    while (sManager->lock) {
    }
    sManager->lock = 1;

    uint16_t chipNum = len / 3;
    uint16_t repeat = chipNum / 8;
    uint8_t rest = chipNum % 8;
    uint8_t brRest = len % 3;

    sManager->sendCache1[0] = 0x55;
    for (i = 0; i < repeat; i++) {
        sManager->sendCache1[1] = 0x80 | (7<<1);
        sManager->sendCache1[2] = id;
        if (YX020PORT_BIT_CHECK(id, 0x01 << 8)) {
            sManager->sendCache1[1] |= 0x01;
        }
        cnt = 3;

        for (j = 0; j < 24; j++) {
            t = br[i*24+j];
            sManager->sendCache1[cnt] = t >> 8;
            cnt++;
            sManager->sendCache1[cnt] = t;
            cnt++;
        }
        sManager->sendCache1[cnt] = CRC8MaxIM(sManager->sendCache1, cnt, 1);
        cnt++;
        UartRawSend(UART_COM1, sManager->sendCache1, cnt);

        id += 8;
    }

    if (rest) {
        sManager->sendCache1[1] = 0x80 | ((rest - 1) << 1);
        sManager->sendCache1[2] = id;
        if (YX020PORT_BIT_CHECK(id, 0x01 << 8)) {
            sManager->sendCache1[1] |= 0x01;
        }
        cnt = 3;
        for (j = 0; j < rest * 3; j++) {
            t = br[i*24+j];
            sManager->sendCache1[cnt] = t >> 8;
            cnt++;
            sManager->sendCache1[cnt] = t;
            cnt++;
        }
        if (brRest != 0) {
            t = sManager->sendCache1[1];
            t &= 0x01;
            sManager->sendCache1[1] >>= 1;
            sManager->sendCache1[1]++;
            sManager->sendCache1[1] <<= 1;
            sManager->sendCache1[1] |= t;
            for (j = 0; j < brRest; j++) {
                t = br[chipNum*3+j];
                sManager->sendCache1[cnt] = t >> 8;
                cnt++;
                sManager->sendCache1[cnt] = t;
                cnt++;
            }
            while (j < 3) {
                sManager->sendCache1[cnt++] = 0x00;
                sManager->sendCache1[cnt++] = 0x00;
                j++;
            }
        }

        sManager->sendCache1[cnt] = CRC8MaxIM(sManager->sendCache1, cnt, 1);
        cnt++;
        UartRawSend(UART_COM1, sManager->sendCache1, cnt);
    }

    sManager->lock = 0;
}

void YX020PortSendBrightness8bit(uint16_t id, const uint8_t *br, uint16_t len)
{
    uint16_t i, j, cnt;
    uint16_t t;

    while (sManager->lock) {
    }
    sManager->lock = 1;

    uint16_t chipNum = len / 3;
    uint16_t repeat = chipNum / 8;
    uint8_t rest = chipNum % 8;
    uint8_t brRest = len % 3;

    sManager->sendCache1[0] = 0x55;
    for (i = 0; i < repeat; i++) {
        sManager->sendCache1[1] = 0x80 | (7<<1);
        sManager->sendCache1[2] = id;
        if (YX020PORT_BIT_CHECK(id, 0x01 << 8)) {
            sManager->sendCache1[1] |= 0x01;
        }
        cnt = 3;

        for (j = 0; j < 24; j++) {
            t = br[i*24+j];
            sManager->sendCache1[cnt] = t;
            cnt++;
        }
        sManager->sendCache1[cnt] = CRC8MaxIM(sManager->sendCache1, cnt, 1);
        cnt++;
        UartRawSend(UART_COM1, sManager->sendCache1, cnt);

        id += 8;
    }

    if (rest) {
        sManager->sendCache1[1] = 0x80 | ((rest - 1) << 1);
        sManager->sendCache1[2] = id;
        if (YX020PORT_BIT_CHECK(id, 0x01 << 8)) {
            sManager->sendCache1[1] |= 0x01;
        }
        cnt = 3;
        for (j = 0; j < rest * 3; j++) {
            t = br[i*24+j];
            sManager->sendCache1[cnt] = t;
            cnt++;
        }
        if (brRest != 0) {
            t = sManager->sendCache1[1];
            t &= 0x01;
            sManager->sendCache1[1] >>= 1;
            sManager->sendCache1[1]++;
            sManager->sendCache1[1] <<= 1;
            sManager->sendCache1[1] |= t;
            for (j = 0; j < brRest; j++) {
                t = br[chipNum*3+j];
                sManager->sendCache1[cnt] = t;
                cnt++;
            }
            while (j < 3) {
                sManager->sendCache1[cnt++] = 0x00;
                sManager->sendCache1[cnt++] = 0x00;
                j++;
            }
        }

        sManager->sendCache1[cnt] = CRC8MaxIM(sManager->sendCache1, cnt, 1);
        cnt++;
        UartRawSend(UART_COM1, sManager->sendCache1, cnt);
    }

    sManager->lock = 0;
}

void YX020PortBroacastBr(uint16_t *br, uint8_t vsync)
{
    uint16_t i;

    while (sManager->lock) {
    }
    sManager->lock = 1;

    sManager->sendCache1[0] = 0x55;
    sManager->sendCache1[1] = 0x81;
    sManager->sendCache1[2] = 0xFF;
    sManager->sendCache1[3] = br[0] >> 8;
    sManager->sendCache1[4] = br[0];
    sManager->sendCache1[5] = br[1] >> 8;
    sManager->sendCache1[6] = br[1];
    sManager->sendCache1[7] = br[2] >> 8;
    sManager->sendCache1[8] = br[2];
    sManager->sendCache1[9] = CRC8MaxIM(sManager->sendCache1, 9, 1);
    UartRawSend(UART_COM1, sManager->sendCache1, 10);

    if (vsync) {
        delayUs(50);
        YX020PortSendVSYNC();
    }

    sManager->lock = 0;
}

void YX020PortReadREG(uint16_t id, uint8_t reg)
{
    uint8_t i;

    while (sManager->lock) {
    }
    sManager->lock = 1;

    sManager->sendCache1[0] = 0x55;
    sManager->sendCache1[1] = 0xA0;
    sManager->sendCache1[1] |= reg << 1;
    if (YX020PORT_BIT_CHECK(id, 0x01 << 9)) {
        sManager->sendCache1[1] |= 0x01;
    }
    sManager->sendCache1[2] = id;
    sManager->sendCache1[3] = CRC8MaxIM(sManager->sendCache1, 3, 1);
    sManager->sendCache1[4] = 0x00;
    sManager->sendCache1[5] = 0x00;

    YX020PortSendCMD2S();
    UartRawSend(UART_COM1, sManager->sendCache1, 6);

    sManager->lock = 0;
}

void YX020PortBroadcastREG(YX020_BROAD_REG_E reg, uint8_t val)
{
    uint8_t i;

    while (sManager->lock) {
    }
    sManager->lock = 1;

    sManager->sendCache1[0] = 0x55;
    sManager->sendCache1[1] = reg;
    sManager->sendCache1[2] = 0xFF;
    sManager->sendCache1[3] = val;
    sManager->sendCache1[4] = CRC8MaxIM(sManager->sendCache1, 4, 1);
    UartRawSend(UART_COM1, sManager->sendCache1, 5);

    sManager->lock = 0;
}

void YX020PortWriteSelectReg(YX020_WSELECT_REG_E reg, uint8_t val, uint16_t id, uint8_t cmd2s)
{
    uint8_t i;

    while (sManager->lock) {
    }
    sManager->lock = 1;

    sManager->sendCache1[0] = 0x55;
    sManager->sendCache1[1] = reg;
    if (YX020PORT_BIT_CHECK(id, (0x01 << 9))) {
        sManager->sendCache1[1] |= 0x01;
    }
    sManager->sendCache1[2] = id;
    sManager->sendCache1[3] = val;
    sManager->sendCache1[4] = CRC8MaxIM(sManager->sendCache1, 4, 1);
    if (cmd2s) {
        YX020PortSendCMD2S();
    }
    UartRawSend(UART_COM1, sManager->sendCache1, 5);

    sManager->lock = 0;
}

void YX020PortSendFrameHead(uint8_t reg4, uint8_t reg7, uint8_t tm)
{
    while (sManager->lock) {
    }
    sManager->lock = 1;

    UartRawSendByte(UART_COM1, 0xF5);
    UartWaitSendDone(UART_COM1);
    delayUs(30);
    UartRawSendByte(UART_COM1, 0x79);
    UartWaitSendDone(UART_COM1);

    if (tm) {
        delayUs(30);
        YX020PortBroadcastREG(YX020_BROAD_REG7, reg7);
        UartWaitSendDone(UART_COM1);
    }

    delayUs(30);
    YX020PortBroadcastREG(YX020_BROAD_REG4, reg4);

    sManager->lock = 0;
}

void YX020PortChipInit(uint8_t reg4, uint16_t chipNum, uint8_t btm, uint8_t reg7, uint8_t cmd2d)
{
    uint16_t i;

    while (sManager->lock) {
    }
    sManager->lock = 1;

    sManager->chipNum = chipNum;
    calDelayCMD2D();

    if (btm) {
        for (i = 0; i < chipNum; i++) {
            YX020PortSendFrameHead(reg4, reg7, 1);
            delayUs(50 + DELAY_OFFSET);
        }
    }
    else {
        for (i = 0; i < chipNum; i++) {
            YX020PortSendFrameHead(reg4, 0, 0);
            delayUs(50 + DELAY_OFFSET);
        }
    }

    delayUs(50 + DELAY_OFFSET);
    YX020PortSendSetID(chipNum);
    delayUs(100 + DELAY_OFFSET);
    if (cmd2d) {
        YX020PortSendCMD2D();
    }

    sManager->lock = 0;
}

void YX020PortFrame(const uint8_t *regs, const uint16_t *br, uint16_t brLen, uint16_t id)
{
    uint16_t ms;
    uint16_t us;

    while (sManager->lock) {
    }
    sManager->lock = 1;

    YX020PortSendFrameHead(regs[4], 0, 0);
    delayUs(50 + DELAY_OFFSET);
    YX020PortSendSetID(1);
    delayUs(20);
    YX020PortSendCMD2D();
    delayUs(200);

    YX020PortBroadcastREG(YX020_BROAD_REG0, regs[0]);
    YX020PortBroadcastREG(YX020_BROAD_REG1, regs[1]);
    YX020PortBroadcastREG(YX020_BROAD_REG2, regs[2]);
    YX020PortBroadcastREG(YX020_BROAD_REG3, regs[3]);

    if (sManager->bit8Mode) {
        YX020PortSendBrightness8bit(id, (uint8_t *)br, brLen);
    }
    else {
        YX020PortSendBrightness(id, br, brLen);
    }

    YX020PortSendVSYNC();
    if (sManager->lowiq) {
        UartIOToCore(UART_COM1, 0);
        UartTxPullDown(UART_COM1);
        if (sManager->pdTime > 1000) {
            ms = sManager->pdTime / 1000;
            us = sManager->pdTime % 1000;
            sManager->delayMs(ms);
            delayUs(us);
        }
        else {
            delayUs(sManager->pdTime);
        }
        UartIOToPeripheral(UART_COM1, 0);
    }

    sManager->lock = 0;
}

void YX020PortFrameHLBaud(const uint8_t *regs, const uint16_t *br, uint16_t brLen, uint16_t id, uint8_t f0)
{
    uint16_t ms;
    uint16_t us;

    while (sManager->lock) {
    }
    sManager->lock = 1;

    if (f0) {
        UartSetBaud(UART_COM1, sManager->lowBaud);
    }
    else {
        UartSetBaud(UART_COM1, sManager->highBaud);
    }
    delayUs(100);
    YX020PortSendSetBaud();
    delayUs(100);

    UartSetBaud(UART_COM1, sManager->lowBaud);
    YX020PortSendFrameHead(regs[4], 0, 0);
    delayUs(50 + DELAY_OFFSET);
    YX020PortSendSetID(1);
    delayUs(100);
    YX020PortSendCMD2D();
    delayUs(100);
    YX020PortSendSetBaud();

    delayUs(100);
    UartSetBaud(UART_COM1, sManager->highBaud);
    delayUs(10);
    UartRawSendByte(UART_COM1, 0xF5);
    UartWaitSendDone(UART_COM1);
    delayUs(30);
    UartRawSendByte(UART_COM1, 0x79);
    UartWaitSendDone(UART_COM1);
    delayUs(30);
    YX020PortBroadcastREG(YX020_BROAD_REG0, regs[0]);
    YX020PortBroadcastREG(YX020_BROAD_REG1, regs[1]);
    YX020PortBroadcastREG(YX020_BROAD_REG2, regs[2]);
    YX020PortBroadcastREG(YX020_BROAD_REG3, regs[3]);

    if (sManager->bit8Mode) {
        YX020PortSendBrightness8bit(id, (uint8_t *)br, brLen);
    }
    else {
        YX020PortSendBrightness(id, br, brLen);
    }

    YX020PortSendVSYNC();
    if (sManager->lowiq) {
        UartIOToCore(UART_COM1, 0);
        UartTxPullDown(UART_COM1);
        if (sManager->pdTime > 1000) {
            ms = sManager->pdTime / 1000;
            us = sManager->pdTime % 1000;
            sManager->delayMs(ms);
            delayUs(us);
        }
        else {
            delayUs(sManager->pdTime);
        }
        UartIOToPeripheral(UART_COM1, 0);
    }

    sManager->lock = 0;
}
